#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 将公共模型映射到数据库表
#
# wencan
# 2019-05-22

import attr
import typing
from datetime import date, datetime, time, timedelta

import sqlalchemy
import sqlalchemy.orm

__all__ = ("sql_table",)

# 标准基础类型到sqlalchemy类型的映射
_sql_types = {
    int: sqlalchemy.Integer,
    str: sqlalchemy.String,
    float: sqlalchemy.Numeric,
    datetime: sqlalchemy.DateTime,
    date: sqlalchemy.Date,
    time: sqlalchemy.Time,
    timedelta: sqlalchemy.Interval,
    bytes: sqlalchemy.Binary,
    bool: sqlalchemy.Boolean,
    # dict: sqlalchemy.JSON,
    # list: sqlalchemy.ARRAY,
}


def map_to_table(cls, table_name: str, md: sqlalchemy.MetaData) -> None:
    '''将通用模型映射为SQL表'''

    # 生成表字段列表
    columns: sqlalchemy.Column = []
    for column in attr.fields(cls):
        # 列名
        column_name = column.name
        # 列键
        column_key = column.name
        # 主键
        primary_key = False

        # 模型元数据
        sql_md = column.metadata.get("sql")
        if sql_md:
            if sql_md == "-":   # 忽略
                continue

            parts = sql_md.split(";")
            # 第一部分为列键
            if parts[0] != column_name:
                column_name = parts[0]

            for part in parts[1:]:
                if part == "primary_key":
                    primary_key = True
        
        # 列类型
        # 应该跳过自定义字段
        _type = _sql_types[column.type]
        
        # 创建sql列
        c = sqlalchemy.Column(column_name, _type, key=column_key, primary_key=primary_key)
        columns.append(c)

    # 创建表对象
    sql_table = sqlalchemy.Table(table_name, md, *columns)
    #  映射
    sqlalchemy.orm.mapper(cls, sql_table)


md = sqlalchemy.MetaData()


def sql_table(table_name: str) -> typing.Callable:
    '''通过装饰，将模型类映射到表'''
    def wrap(cls):
        map_to_table(cls, table_name, md)
        return cls
    return wrap